#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<math.h>
#include"Stack.h"
//gcc assert



//1 stack adress variable is damaged
//2 problems with len (<0, >max)
//3 impossible to broaden
//4 stack is full
//5 stack wasn't poisoned
int stack_ctor(struct stack_t* stk)
{
    //if (stk->stack != 0) free(stk->stack);

    stk->len = 0;

    stk->stack = (double*) calloc(2, sizeof(double));

    stk->maxSize = 2;
    if (!stk->stack)
        stack_error(stk, STKERR_BROADEN);

    //ASSERT(!stack_OK(stk));


    stk->stack[0] = stk->stack[1] = POISON;
    return 0;
}
void stack_dtor(struct stack_t* stk)
{

    if (stk->stack != 0)free(stk->stack);
    stk->len = 0;
    stk->maxSize = 0;
    stk->stack = NULL;
}
int stack_OK( struct stack_t* stk)
{

    if (stk->len < 0 || stk->len > stk->maxSize)
        return STKERR_LEN;
    if (stk->stack == 0)
        return STKERR_ADRESS;
    return 0;
}
int stack_push(struct stack_t* stk, double value)
{
    ASSERT(!stack_OK(stk))

    if (stk->len == stk->maxSize - 1)
    {
        if(stack_broaden(stk))
            return STKERR_BROADEN;
        else
        {
            stk->len++;
            if(stk->stack[stk->len] == POISON)
                stk->stack[stk->len] = value;
            else
                return STKERR_FULL;
        }

    }
    else
    {
        stk->len++;
        if(stk->stack[stk->len] == POISON)
            stk->stack[stk->len] = value;

        else
            return STKERR_NOTPOISONED;
    }
    return 0;
}
double stack_pop (struct stack_t* stk, int* err)
{
    ASSERT(!stack_OK(stk));

    double temp = stk->stack[stk->len];
    if(stk->len == 0) {printf("ERROR: TRYING TO POP FROM THE EMPTY STACK"); abort();}
    if(temp == POISON)
    {
        printf("\nCRITICAL ERROR: STACK WAS DAMAGED. TOP ELEMENT IS POISONED. PROGRAMM WILL BE ABORTED\n");
        abort();
    }
    stk->stack[stk->len] = POISON;
    stk->len--;
    ASSERT(!stack_OK(stk));
    return temp;
}
int stack_broaden(struct stack_t* stk)
{

    double* temp = stk->stack;
    if (temp = (double*) realloc(temp, 2 * stk->maxSize * sizeof(double)))
    {
        stk->stack = temp;
        for(int i = stk->maxSize; i < stk->maxSize*2; i++)
            stk->stack[i] = POISON;
        stk->maxSize *= 2;
        return 0;
    }
    else
        return STKERR_BROADEN;
}

int  smartFileReader(struct stack_t* stk)
{
    FILE* f = NULL;
    if (!(f = fopen("input.txt", "r")))
    {printf("ERROR: No input"); abort();}

    FILE* f_out = NULL;
    if (!(f = fopen("output.txt", "w+")))
    {printf("ERROR: No input"); abort();}

    char command[10]; double value = 0;
    int err = 0, err1 = 0, err2 = 0;

    fscanf(f,"%s", command);
    while(strcmp(command,"Quit"))
    {

        if (!strcmp(command,"push"))
        {
            fscanf(f, "%lg", &value);
            if(value != POISON)
                stack_push(stk, value);

            else
                printf("Sorry You've used poisoned value. Change it, please.");
        }
        else if(!strcmp(command, "pop"))
        {
            fprintf(f_out,"%lg\n", stack_pop(stk, &err));
            stack_error(stk, err);
        }
        else if(!strcmp(command, "add"))
        {
            value = stack_pop(stk, &err);
            stack_error(stk, err);

            value+= stack_pop(stk, &err);
            stack_error(stk, err);

            stack_error(stk, stack_push(stk, value));
        }
        else if(!strcmp(command, "sub"))
        {
            value = stack_pop(stk, &err);
            stack_error(stk, err);
            value -= stack_pop(stk, &err);
            stack_error(stk, err);
            stack_error(stk, stack_push(stk, value));
        }
        else if(!strcmp(command, "mul"))
        {
            value = stack_pop(stk, &err);
            stack_error(stk, err);

            value *= stack_pop(stk, &err);
            stack_error(stk, err);

            stack_error(stk, stack_push(stk, value));
        }
        else if(!strcmp(command, "div"))
        {
            value = stack_pop(stk, &err);
            stack_error(stk, err);

            double value1 = stack_pop(stk, &err);
            stack_error(stk, err);

            if (value1) value /= value1;
            else
            {
                printf("ERROR: TRYING TO DIV 0");
                abort();
            }

            stack_error(stk, stack_push(stk, value));
        }
        else if(!strcmp(command, "sin"))
        {
            value = stack_pop(stk, &err);
            stack_error(stk, err);

            value = sin(value);

            stack_error(stk, stack_push(stk, value));
        }
        else if(!strcmp(command, "cos"))
        {
            value = stack_pop(stk, &err);
            stack_error(stk, err);

            value = cos(value);

            stack_error(stk, stack_push(stk, value));
        }
        else {printf("ERROR: NO SUCH COMMAND <%s>", command); abort();}

        fscanf(f,"%s", command);
    }
    fclose(f); fclose(f_out);
}
//1 stack adress variable is damaged
//2 problems with len (<0, >max)
//3 impossible to broaden
//
//5 stack wasn't poisoned
void stack_error  (struct stack_t* stk, int err)
{
    if (err)
    {
        stack_bump(stk);
        printf("ERROR %d: ", err);
        switch (err)
        {
            case STKERR_ADRESS:
                printf("stack_t.stack variable is damaged");
                abort();
                break;
            case STKERR_LEN:
                printf("stack_t.len is damaged(len is <0 or >0)");
                abort();
                break;
            case STKERR_BROADEN:
                printf("Impossible to broaden the stack");
                abort();
                break;
            case STKERR_FULL:
                printf("Stak is full. Unable to write");
                abort;
                break;
            case STKERR_NOTPOISONED:
                printf("Stack wasn't POISONED");
                abort();
                break;
            default:
                abort();

        }
    }
}
void stack_bump   (struct stack_t* stk)
{
    printf("Stack_OK(%d)\nStack:\n", stack_OK(stk));
    if (stk)
    {
        for (int i = 1; i <= stk->len;i++)
            printf("    data[%d] = %lg\n", i, stk->stack[i]);
        printf("stk.len =  %d; stk.maxLength = %d \nNote:0th element is Poisoned\n", stk->len, stk->maxSize);
    }
}
